package no.sachmuld.methodcombination.api.reflection.component;

import no.sachmuld.methodcombination.api.BehaviourChain;
import no.sachmuld.methodcombination.api.BehaviourMethod;
import no.sachmuld.methodcombination.api.Direction;
import no.sachmuld.methodcombination.api.MethodInvocation;
import no.sachmuld.methodcombination.impl.AbstractMethodInvocation;

import java.lang.reflect.Method;

/**
 *
 */
public class PrimaryMethodCombination<B, R> extends DelegatingMethodCombination<B, R> {

    final private Direction direction;

    public PrimaryMethodCombination(Direction direction) {
        this.direction = direction;
    }

    @Override
    public PrimaryMethodCombination<B, R> build(final Class behaviourClass, final Method targetMethod) {

        final BehaviourMethod primaryMethod = new BehaviourMethod.Builder(behaviourClass, targetMethod, true).extraParameterTypes(MethodInvocation.class).build();

        validatedAgainst(primaryMethod);

        executedBy(new AbstractMethodCombination<B, R>() {
            @Override
            public R execute(final Object targetObject, BehaviourChain<? extends B> behaviourChain, final Object... parameters) {
                return new AbstractMethodInvocation<B, R>(behaviourChain.iterable(direction), targetObject, targetMethod, parameters) {
                    @Override
                    protected R executeBehaviour(B behaviour) {
                        if (behaviourClass.isAssignableFrom(behaviour.getClass())) {
                            return (R) primaryMethod.invoke(behaviour, parameters, this);
                        } else {
                            return proceed();
                        }
                    }
                }.execute();
            }
        });

        return this;

    }

}
